package org.aplikator.client.local.widgets;

import com.github.gwtbootstrap.client.ui.Form;
import com.github.gwtbootstrap.client.ui.constants.FormType;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import org.aplikator.client.local.Aplikator;
import org.aplikator.client.shared.data.*;
import org.aplikator.client.shared.descriptor.FormDTO;
import org.aplikator.client.shared.descriptor.PropertyDTO;
import org.aplikator.client.shared.descriptor.ViewDTO;
import org.jboss.errai.common.client.api.RemoteCallback;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("rawtypes")
public class FormWidget extends AbstractFormContainerWidget implements  HasFields {

    private List<DataField<?>> fields = new ArrayList<DataField<?>>();
    private List<NestedCollectionWidget> collections = new ArrayList<NestedCollectionWidget>();
    private List<BinaryFieldWidget> binaryFields = new ArrayList<BinaryFieldWidget>();
    private List<FunctionButtonWidget> functionButtons = new ArrayList<FunctionButtonWidget>();
    private ViewDTO view;
    private Record original;
    private Record edited;
    private boolean dirty = false;
    private RecordPage table;
    private HasFields ownerForm;
    RecordContainer recordContainer;



    public FormWidget(ViewDTO view, RecordPage table, HasFields ownerForm) {
        this.view = view;
        this.table = table;
        this.ownerForm = ownerForm;
        Form wrapper = new Form();

        FormDTO descriptor = view.getFormDescriptor();
        if (descriptor.isHorizontal()){
            wrapper.setType(FormType.HORIZONTAL);
        } else {
            wrapper.setType(FormType.VERTICAL);
        }
        initializeForm(descriptor, wrapper);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void registerDataField(DataField<? extends Object> field) {
        fields.add(field);
        field.addValueChangeHandler(this);
    }

    @Override
    public void registerNestedCollection(NestedCollectionWidget collection) {
        collections.add(collection);
    }

    @Override
    public void registerBinaryField(BinaryFieldWidget binaryField) {
        binaryFields.add(binaryField);
    }

    @Override
    public void registerFunctionButton(FunctionButtonWidget functionButton) {
    	functionButtons.add(functionButton);
    }

    public void setEnabled(boolean enabled){
        if (enabled) return;  //we want only to disable nested fields, not to re-enable
        for (DataField field: fields){
            field.setEnabled(enabled);
        }
    }

    @SuppressWarnings({ "unchecked" })
    public void onValueChange(ValueChangeEvent event) {
        setDirty(true);
        if (ownerForm != null) {
            ownerForm.onValueChange(event);
        }
        if (event.getSource().getClass().equals(ReferenceFieldWidget.class)) {
            ReferenceFieldWidget source = (ReferenceFieldWidget) event.getSource();
            Record reference = source.getSelectedRecord();
            if (reference != null) {
                for (DataField field : fields) {
                    PropertyDTO prop = field.getProperty();
                    if (prop.getRefferedThrough() != null) {// TODO kontrolovat
                                                            // pres co je
                                                            // referovano...
                        PropertyDTO unreferencedProperty = prop.cloneUnreferenced();
                        Serializable val = (Serializable) unreferencedProperty.getValue(reference);
                        if (val != null) {
                            field.setValue(val);
                        }
                    }
                }
            }
        }
    }

    public void displayRecord(final PrimaryKey primaryKey, final RecordContainer recordContainer) {
        this.recordContainer = recordContainer;
        if (primaryKey != null && primaryKey.getId() != -1) {
            Aplikator.aplikatorService.call( new RemoteCallback<Record>() {
                @Override
                public void callback(Record record) {
                    original = record;
                    populateFormWithData(false);
                }
            }).getRecord(primaryKey.getSerializationString(),view.getId());
        } else {
            original = recordContainer.findLastUpdated(primaryKey);
            if (original != null) populateFormWithData(false);
            //original = new Record();
        }
    }

    public void addRecord(final RecordContainer recordContainer, final PropertyDTO ownerProperty, final PrimaryKey ownerPrimaryKey, final Record toCopy) {
        this.recordContainer = recordContainer;
        Aplikator.aplikatorService.call( new RemoteCallback<Record>() {
            public void callback(Record record) {
                original = record;
                original.setOwnerPropertyId(ownerProperty!=null?ownerProperty.getId():null);
                original.setOwnerPrimaryKey(ownerPrimaryKey);
                populateFormWithData(true);
                recordContainer.addRecord(view, original, original, Operation.CREATE);
                if (ownerForm != null && table != null) {
                    table.addRecord(original);
                }
            }
        }).prepareRecord(toCopy!= null?toCopy.getPrimaryKey().getSerializationString():null, view.getId());
    }

    
    @SuppressWarnings({ "unchecked" })
    public void populateFormWithData(boolean setDirty) {
        setDirty(setDirty);
        this.populateFormWithSimpleData(setDirty);
        for (NestedCollectionWidget collection : collections) {
            collection.setRecordContainer(recordContainer);
            collection.setOwnerPrimaryKey(original.getPrimaryKey());
            collection.setDirty(setDirty);
        }
        for (BinaryFieldWidget binaryField : binaryFields) {
            binaryField.setPrimaryKey(original.getPrimaryKey());
        }
    }

    public void onClose(Command afterConfirmation) {
        if (dirty) {
            if (ownerForm == null) {
                final DialogBox dialogBox = createDialogBox(afterConfirmation);
                dialogBox.setAnimationEnabled(true);
                dialogBox.center();
            } else {
                save();
                ownerForm.setDirty(true);
                if (table != null) {
                    table.updateRecord(edited);
                }
                afterConfirmation.execute();
            }
        } else {
            afterConfirmation.execute();
        }

    }

    @Override
    public Record getPopulateRecord() {
        return getOriginal();
    }

    
    
    private DialogBox createDialogBox(final Command afterConfirmation) {
        // Create a dialog box and set the caption text

        // Create a table to layout the content
        DockLayoutPanel dialogContents = new DockLayoutPanel(Unit.EM);
        final DialogBox dialogBox = new DialogBox();
        dialogBox.setText("There are unsaved changes. Save?");
        dialogBox.setWidget(dialogContents);
        FlowPanel buttonPanel = new FlowPanel();
        // Add a close button at the bottom of the dialog
        Button closeButton = new Button("Cancel", new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
            }
        });
        buttonPanel.add(closeButton);

        // Add a Donotsave button at the bottom of the dialog
        Button doNotSaveButton = new Button("Don't Save", new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                afterConfirmation.execute();
            }
        });
        buttonPanel.add(doNotSaveButton);

        // Add a save button at the bottom of the dialog
        Button saveButton = new Button("Save", new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                save();
                afterConfirmation.execute();
            }
        });
        buttonPanel.add(saveButton);
        dialogContents.addSouth(buttonPanel, 2);
        dialogContents.setSize("300px", "3em");

        // Return the dialog box
        return dialogBox;
    }

    @SuppressWarnings({ "unchecked" })
    public void save() {

        PrimaryKey pk = new PrimaryKey(DEBUG_ID_PREFIX, DEBUG_ID_PREFIX);
        
        edited = new Record(original.getPrimaryKey());
        edited.setOwnerPrimaryKey(original.getOwnerPrimaryKey());
        edited.setOwnerPropertyId(original.getOwnerPropertyId());
        for (DataField field : fields) {
            if (field.isDirty()){
                PropertyDTO prop = field.getProperty();
                Serializable val = field.getValue();
                prop.setValue(edited, val);
            }
        }
        recordContainer.addRecord(view, original, edited, Operation.UPDATE);
        for (NestedCollectionWidget collection : collections) {
            collection.save();
        }
        if (ownerForm == null) {
            Aplikator.aplikatorService.call( new RemoteCallback<RecordContainer>() {
                public void callback(RecordContainer records) {
                    for (ContainerNode node : records.getRecords()) {
                        if (Operation.DELETE.equals(node.getOperation())) {
                            continue;
                        }
                        if (node.getOriginal().getPrimaryKey().equals(original.getPrimaryKey())) {
                            original = node.getOriginal();
                            for (DataField field : fields) {
                                PropertyDTO prop = field.getProperty();
                                Serializable val = (Serializable) prop.getValue(original);
                                field.setValue(val);
                                field.setDirty(false);
                            }
                            for (NestedCollectionWidget collection : collections) {
                                collection.setDirty(false);
                            }
                            if (table != null) {
                                if (Operation.CREATE.equals(node.getOperation())) {
                                    table.addRecord(original);
                                } else if (Operation.UPDATE.equals(node.getOperation())) {
                                    table.updateRecord(original);
                                }
                            }
                        }
                    }
                    setDirty(false);
                }
            }).processRecords(recordContainer);
            recordContainer.clear();
        }
    }

    @Override
    public Record getOriginal() {
        return original;
    }

    @Override
    public void setDirty(boolean dirty) {
        this.dirty = dirty;
        for (FunctionButtonWidget button : functionButtons) {
            button.setEnabled(!dirty);
        }
    }

    public void setNestedDirty(boolean dirty) {
        setDirty(dirty);
        for (DataField field : fields) {
            field.setDirty(dirty);
        }
    }

    @Override
    public List<DataField<?>> getDataFields() {
        return this.fields;
    }

    
}
